package com.zuikaku.shardingsql.util;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;

public class ShardingUtil {

    /**
     * 分表数量
     */
    private static int shardTableNum;
    /**
     * 分库数量
     */
    private static int shardDbNum;

    /**
     * 分库下标识占位符号
     */
    private static  String shardMark;
    /**
     * SQL文件后缀
     */
    private static final String FILE_SQL = ".sql";

    /**
     * 分库的目标域（用于输出）
     */
    private static List<String> shardHostList;


    /**
     * 分库SQL输出目录
     */
    private static String outputPath;

    static {
        //水平拆分
        ResourceBundle rb = ResourceBundle.getBundle("config");
        outputPath =new String(rb.getString("output.path").getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);////中文转义
        shardTableNum=Integer.parseInt(rb.getString("shard.tableNum"));
        shardDbNum =Integer.parseInt(rb.getString("shard.dbNum"));
        shardMark = rb.getString("shard.mark");
        String []shardHostArr = new String (rb.getString("shard.host").getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8).split(",");
        shardHostList =  Arrays.asList(shardHostArr);
        if(shardHostList.size()!=shardDbNum){
            System.err.println("分库数量应与目标域的数量相同");
            System.exit(0);
        }


    }

    /**
     * 转换为分库sql
     * @param dataBaseName
     * @param isAdd 追加模式
     * @throws IOException
     */
    public static void changeSql2ShardSql(String dataBaseName, boolean isAdd,String separator) throws IOException {
        //输出文件位置
        File outFileDir = new File(outputPath);
        if (!outFileDir.exists()) {
            boolean isOk = FileUtil.createDir(outputPath);
            if(!isOk) {
                System.err.println("目标文件夹创建失败：" + outputPath);
                return;
            }
        }
        //建库SQL准备
        BufferedWriter dbWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputPath+ File.separator+"initShardDB"+FILE_SQL, isAdd), StandardCharsets.UTF_8));

        for (int dataBaseIndex = 0; dataBaseIndex < shardDbNum; dataBaseIndex++) {
            dbWriter.write("DROP DATABASE IF EXISTS "+dataBaseName+separator+dataBaseIndex+";");
            dbWriter.newLine();
            dbWriter.write("CREATE DATABASE IF NOT EXISTS "+dataBaseName+separator+dataBaseIndex+" DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;");
            dbWriter.newLine();
            boolean newFileFlag=true;
            //确定数据库名+
            //有分库的时候添加后缀
            String dbIndex = dataBaseIndex >= 10 ? Integer.toString(dataBaseIndex) :  Integer.toString(dataBaseIndex);
            //最终的数据库名
            String dataBaseFinalName = dataBaseName;
            //添加切换数据库
            if (shardDbNum > 1) {
                dataBaseFinalName = dataBaseName + separator+ dbIndex;
            }
            //输出文件名
            String dataBaseIP=shardHostList.get(dataBaseIndex);
            String outPutFileFolder=outputPath + File.separator + dataBaseIP;
            FileUtil.createDir(outPutFileFolder);
            //文件名命名规则
            //2 按照库名
            String outFileFinalPath = outPutFileFolder + File.separator + dataBaseFinalName + FILE_SQL;

            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFileFinalPath, isAdd), StandardCharsets.UTF_8));
            try {
                //文件第一行加入使用哪个数据库的语句
                if(newFileFlag&&shardDbNum>1){
                    writer.write("USE " + dataBaseFinalName + ";");
                    writer.newLine();
                    newFileFlag=false;
                }

                //输入SQL的条数
                int inputSQLNum = 0;
                //输出SQL的条数
                int outPutSQLNum = 0;
                //循环取出数据
                String readStr;
                //为每个分库创建String构造器
                StringBuilder inputSQLBuilder = new StringBuilder();
                //读取文件(字符流)
                String inFileFinalPath = FileUtil.getClassPath()+File.separator + "inputFile";
                File[] sqlTemplateFiles = new File(inFileFinalPath).listFiles(pathname -> pathname.getName().endsWith(".sql"));
                for (File sqlFile:sqlTemplateFiles
                     ) {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sqlFile), StandardCharsets.UTF_8));
                    try {
                        //读取inputFile下的模板sql文件，以每一条sql语句（以;结尾为基本单位）
                        while ((readStr = reader.readLine()) != null) {
                            inputSQLBuilder.append(readStr);
                            if (!isEndSqlScript(readStr)) {
                                inputSQLBuilder.append("\r\n");
                                //没有读取完整SQL等待下次读取
                                continue;
                            }
                            //检测到读取完整SQL+1
                            inputSQLNum++;
                            String inputSQLStr = inputSQLBuilder.toString();
                            String outputSQLStr;
                            if (!isNeedShardTable(inputSQLStr)) {
                                //不需要分表的直接输出
                                outputSQLStr = inputSQLStr;
                                writer.write(outputSQLStr);
                                writer.newLine();
                                outPutSQLNum++;
                                //清空SQL存储器
                                inputSQLBuilder.delete(0, inputSQLBuilder.length());
                            } else {
                                //需要分表的逻辑如下
                                for (int tableIndex = 0; tableIndex < shardTableNum; tableIndex++) {
                                    if (shardDbNum != 1 && tableIndex % shardDbNum != dataBaseIndex) {
                                        //不是应该落在本库上的表不写
                                        continue;
                                    }
                                    String tableStr = String.valueOf(tableIndex);
                                    outputSQLStr = inputSQLStr.replaceAll(shardMark, separator + tableStr);
                                    writer.write(outputSQLStr);
                                    writer.newLine();
                                    outPutSQLNum++;
                                    //清空SQL存储器
                                    inputSQLBuilder.delete(0, inputSQLBuilder.length());

                                }
                            }

                        }
                    } finally {
                        reader.close();
                    }
                    System.out.println("DB:" + dataBaseFinalName + " inputSQLNum:" + inputSQLNum + " outPutSQLNum:" + outPutSQLNum);
                }

            } finally {
                //缓存落地
                writer.flush();
                //关闭流
                writer.close();

            }

        }
        dbWriter.flush();
        dbWriter.close();

    }

    //是否为一个SQL语句的执行结束
    private static boolean isEndSqlScript(String lineWriteStr) {
        return lineWriteStr.indexOf(";") != -1;
    }


    private static boolean isNeedShardTable(String readStr) {
        return readStr.indexOf(shardMark) != -1;
    }
}
